package sebastiankerckhof.render.raytrace;

import sebastiankerckhof.CgPanel;
import sebastiankerckhof.render.Renderer;
import sebastiankerckhof.scene.FieldOfView;
import sebastiankerckhof.scene.Scene;
import sebastiankerckhof.structure.Color3f;
import sebastiankerckhof.structure.math.Point3f;
import sebastiankerckhof.structure.math.Vector3f;

//TODO: Optimize for threads
public class RayTracer implements Renderer{

	Scene scene;
	CgPanel panel;
	FieldOfView fov;
	
	@Override
  public void render(Scene scene, CgPanel panel, FieldOfView fov) {
		
		this.scene = scene;
		this.panel = panel;
		this.fov = fov;
		
	  for(int x = 0; x < fov.nx; x++){
	  	updateU(x);
	  	for(int y = 0; y < fov.ny; y++){
	  		Ray r = generateRay(y);
	  		System.out.println(r);
	  		Hit hit = scene.intersect(r, fov.near, Hit.NO_HIT.clone());

        if (hit.distance == Hit.INFINITY){
        	panel.drawPixel(x,y, this.scene.getBackgroundColor().x,
							this.scene.getBackgroundColor().y, this.scene
									.getBackgroundColor().z);
        }else{
        	
        	
        	//TODO: rewrite below
      		Color3f color = scene.light.color //
      				.scale(scene.light.intensity) //cl
      				.multiply(scene.material.color);//cl.cr
      		
      		Point3f hitPoint = r.origin.plus(r.direction.scale(hit.distance)).add(hit.normal.scale(1e-12f)); //TODO: check use of this

      		
      		Vector3f direction = scene.light.position.subtract(hitPoint);
      		direction = direction.normal(); //l
      		
      		float temp = hit.normal.dotProduct(direction);// n.l
      		
      		temp = Math.max(0, -temp);// max(0,n.l)
      		
      		color = color.scale(temp);//cl.cr.max(0,n.l)
      		
      	color = color.clamp();
      		
      		panel.drawPixel(x, y, color.x, color.y, color.z);
      		
        }

 
	  	}
	  }
	  
  }
	
	float u;
	protected void updateU(int x){
		this.u = (float) (fov.left + ((fov.right - fov.left) * (x + 0.5) * (1.0f / fov.nx)));
	}
	

	public Ray generateRay(int y){
		Ray r = new Ray();
		
		float v = (float) (fov.bottom + ((fov.top - fov.bottom) * (y + 0.5) * (1.0f / fov.ny)));
		//TODO: orthograpic view

		r.direction = 
    new Vector3f()        				
		.plus(scene.camera.w.scale(fov.near))
            				.plus(scene.camera.u.scale(u))
            				.plus(scene.camera.v.scale(v));
		

		
		r.origin = (Point3f) scene.camera.e.clone();
		return r;
	}

}
